package com.bwie.service.impl;

import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.RandomUtil;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.ObjectMetadata;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bwie.config.OSSConfig;
import com.bwie.mapper.MenuMapper;
import com.bwie.pojo.Menu;
import com.bwie.pojo.User;
import com.bwie.service.UserService;
import com.bwie.mapper.UserMapper;
import com.bwie.utils.MobileMessageUtil;
import com.bwie.utils.R;
import com.bwie.vo.LoginVo;
import com.bwie.vo.PageInfoVo;
import com.bwie.vo.RegInfoVo;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author 陶仕豪
 * @description 针对表【tb_user(用户表)】的数据库操作Service实现
 * @createDate 2025-07-18 11:34:06
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {
    @Autowired
    UserMapper userMapper;
    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    OSSClient ossClient;
    @Autowired
    OSSConfig ossConfig;
    @Autowired
    HttpServletRequest request;



    public static final String[] CONTENT_TYPES = new String[]{
            "bmp",
            "png",
            "jpeg",
            "jpg",
            "gif",
    };

    @Override
    public R sendCode(String mobile) {
        //生成验证码

        String code = RandomUtil.randomNumbers(4);
        log.info("code=" + code);
        //存入缓存
        stringRedisTemplate.opsForValue().set(mobile, code, 5, TimeUnit.MINUTES);
        //发送验证码
        MobileMessageUtil.sendMobileMessage(mobile, code);
        return R.OK();
    }

    @Override
    public R userReg(RegInfoVo regInfoVo) {
        User user = userMapper.selectOne(new QueryWrapper<User>().lambda().eq(User::getUserMobile, regInfoVo.getMobile()));
        if (user != null) {
            return R.ERROR(500, "用户已注册,不能二次注册");
        }
        //校验验证码
        String code = stringRedisTemplate.opsForValue().get(regInfoVo.getMobile());
        if (code == null) {
            return R.ERROR(500, "验证码已过期");
        }
        if (!code.equals(regInfoVo.getCode())) {

            return R.ERROR(500, "密码错误");
        }


        //加密密码
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        String hashPass = encoder.encode(regInfoVo.getPass());
        //用户注册
        user = new User();
        user.setUserPass(hashPass);
        user.setUserMobile(regInfoVo.getMobile());
        user.setUserName("匿名用户");
        user.setCreateTime(new Date());
        user.setCreateBy("系统");
        userMapper.insert(user);
        return R.OK();

    }

    @Override
    public R login(LoginVo loginVo) {


        long count = 1;

        User user = userMapper.selectOne(new QueryWrapper<User>().lambda().eq(User::getUserMobile, loginVo.getMobile()));
        if (user == null) {
            return R.ERROR(500, "用户不存在,请先注册");
        }
        String key1 = "login-" + user.getUserId();
        String key2 = "lock-" + user.getUserId();
        if (stringRedisTemplate.hasKey(key2)) {
            return R.ERROR(500, "错误次数超过三次,请一分钟后再试");
        }

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        boolean check = encoder.matches(loginVo.getPass(), user.getUserPass());
        if (!check) {

            Boolean hasKey = stringRedisTemplate.hasKey(key1);
            if (hasKey) {
                count = stringRedisTemplate.opsForValue().increment(key1);
                if (count > 3) {
                    stringRedisTemplate.delete(key1);
                    stringRedisTemplate.opsForValue().set(key2, "1", 1, TimeUnit.MINUTES);
                    return R.ERROR(500, "错误次数超过三次,请一分钟后再试");
                }
            } else {
                stringRedisTemplate.opsForValue().set(key1, "1", 5, TimeUnit.MINUTES);
            }
            return R.ERROR(500, "密码错误还有" + (3 - count) + "次机会");
        }


        String token = Jwts.builder().signWith(SignatureAlgorithm.HS256, "123456")
                .claim("userName", user.getUserName())
                .claim("userId", user.getUserId())
                .compact();
        String key = "token-" + user.getUserId();
        stringRedisTemplate.opsForValue().set(key, token, 60, TimeUnit.MINUTES);

        //获取当前用户的角色列表(权限菜单)
        List<String> roleList = userMapper.getRoleList(user.getUserId());
        user.setRoleList(roleList);
        user.setToken(token);
        user.setUserPass(null);
        return R.OK(user);
    }

    @Override
    public R uploadFile(MultipartFile file) {
        String url = "";
        Boolean flag = false;
        for (String contentType : CONTENT_TYPES) {
            if (StringUtils.endsWithIgnoreCase(file.getOriginalFilename(), contentType)) {
                flag = true;
                break;
            }
        }
        if (!flag) {
            return R.OK(url);
        }
        String originalFilename = file.getOriginalFilename();
        String fileType = originalFilename.substring(originalFilename.lastIndexOf("."));

        String datePath = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
        String newFileUrl = ossConfig.getFindHost() + "/" + datePath + "/" + UUID.randomUUID().toString().replace("-", "") + fileType;
        try {
            InputStream inputStream = file.getInputStream();
            ObjectMetadata objectMetadata = new ObjectMetadata();
            objectMetadata.setContentType("image/jpg");
            ossClient.putObject(ossConfig.getBucketName(), newFileUrl, inputStream, objectMetadata);
            url = "http://" + ossConfig.getBucketName() + "." + ossConfig.getEndpoint() + "/" + newFileUrl;
            return R.OK(url);

        } catch (Exception e) {
            e.printStackTrace();
            return R.ERROR(500, "上传失败");
        }
    }

    @Override
    public R getMenuTree() {
        String token = request.getHeader("token");
        if(token==null){
            return R.ERROR(500,"请先登录");
        }
        Integer userId = Jwts.parser().setSigningKey("123456").parseClaimsJws(token).getBody().get("userId", Integer.class);
        //获取菜单列表
        List<Menu> menuList=userMapper.getMenuTree(userId);

        List<Tree<String>> treeList = TreeUtil.build(menuList, "0", (tree, menu) -> {
            menu.setId(tree.getMenuId()+"");
            menu.setName(tree.getMenuName()+"");
            menu.setParentId(tree.getMenuPid()+"");
            menu.putExtra("path",tree.getMenuPath());
        });
        return R.OK(treeList);
    }

    @Override
    public R getMenu() {
        String token = request.getHeader("token");
        if(token==null){
            return R.ERROR(500,"请先登录");
        }
        Integer userId = Jwts.parser().setSigningKey("123456").parseClaimsJws(token).getBody().get("userId", Integer.class);
        //获取菜单列表
        List<Menu> menuList=userMapper.getMenuTree(userId);

        ArrayList<Menu> root = new ArrayList<>();
        for (Menu menu : menuList) {
            if(menu.getMenuPid()==0){
                root.add(menu);
            }
        }
        Tree(root,menuList);
        return R.OK(root);
    }

    @Override
    public R getShopperList(PageInfoVo pageInfoVo) {
        Page<User> userPage = new Page<>(pageInfoVo.getPageNum(), pageInfoVo.getPageSize());
        Page<User> page= userMapper.getShopperList(userPage,pageInfoVo);
        return R.OK(page);
    }

    public void Tree(List<Menu> root,List<Menu> listAll){
        for (Menu menu : root) {
            ArrayList<Menu> children = new ArrayList<>();
            for (Menu menuAll : listAll) {
                if(menuAll.getMenuPid().equals(menu.getMenuId())){
                children.add(menuAll);
                }

            }
            menu.setChildren(children);
            Tree(children,listAll);
        }

    }
}




